// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;

namespace Swaggatherer
{
    internal static class Template
    {
        public static string Execute(IReadOnlyList<RouteEntry> entries)
        {
            var controllerCount = 0;
            var templatesVisited = new Dictionary<string, (int ControllerIndex, int ActionIndex)>(
                StringComparer.OrdinalIgnoreCase);

            var setupEndpointsLines = new List<string>();
            for (var i = 0; i < entries.Count; i++)
            {
                var entry = entries[i];

                // In attribute routing, same template is used for all actions within that controller. The following
                // simulates that where we only increment the controller count when a new endpoint for a new template
                // is being created.
                var template = entry.Template.TemplateText;
                if (!templatesVisited.TryGetValue(template, out var visitedTemplateInfo))
                {
                    controllerCount++;
                    visitedTemplateInfo = (controllerCount, 0);
                }

                // Increment the action count within a controller template
                visitedTemplateInfo.ActionIndex++;
                templatesVisited[template] = visitedTemplateInfo;

                var controllerName = $"Controller{visitedTemplateInfo.ControllerIndex}";
                var actionName = $"Action{visitedTemplateInfo.ActionIndex}";

                var httpMethodText = entry.Method == null ? "httpMethod: null" : $"\"{entry.Method.ToUpperInvariant()}\"";
                setupEndpointsLines.Add($"            Endpoints[{i}] = CreateEndpoint(\"{template}\", \"{controllerName}\", \"{actionName}\", {httpMethodText});");
            }

            var setupRequestsLines = new List<string>();
            for (var i = 0; i < entries.Count; i++)
            {
                var entry = entries[i];
                setupRequestsLines.Add($"            Requests[{i}] = new DefaultHttpContext();");
                setupRequestsLines.Add($"            Requests[{i}].RequestServices = CreateServices();");

                if (entry.Method != null)
                {
                    setupRequestsLines.Add($"            Requests[{i}].Request.Method = HttpMethods.GetCanonicalizedValue({entries[i].Method});");
                }

                setupRequestsLines.Add($"            Requests[{i}].Request.Path = \"{entries[i].RequestUrl}\";");
            }

            var setupMatcherLines = new List<string>();
            for (var i = 0; i < entries.Count; i++)
            {
                setupMatcherLines.Add($"            builder.AddEndpoint(Endpoints[{i}]);");
            }

            return string.Format(@"
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Matching;

namespace Microsoft.AspNetCore.Routing
{{
    // This code was generated by the Swaggatherer
    public partial class GeneratedBenchmark : EndpointRoutingBenchmarkBase
    {{
        private protected const int EndpointCount = {3};

        private protected void SetupEndpoints()
        {{
            Endpoints = new RouteEndpoint[{3}];
{0}
        }}

        private protected void SetupRequests()
        {{
            Requests = new HttpContext[{3}];
{1}
        }}

        private protected Matcher SetupMatcher(MatcherBuilder builder)
        {{
{2}
            return builder.Build();
        }}

        private RouteEndpoint CreateEndpoint(string template, string controllerName, string actionName, string httpMethod)
        {{
            var requiredValues = new
            {{
                area = (string)null,
                controller = controllerName,
                action = actionName,
                page = (string)null
            }};
            var defaults = new
            {{
                area = (string)null,
                controller = controllerName,
                action = actionName,
                page = (string)null
            }};

            var metadata = new List<object>();
            if (httpMethod != null)
            {{
                metadata.Add(new HttpMethodMetadata(new string[] {{ httpMethod }}));
            }}

            return CreateEndpoint(
                template, 
                defaults: defaults, 
                requiredValues: requiredValues, 
                metadata: metadata, 
                routeName: controllerName);
        }}
    }}
}}",
    string.Join(Environment.NewLine, setupEndpointsLines),
    string.Join(Environment.NewLine, setupRequestsLines),
    string.Join(Environment.NewLine, setupMatcherLines),
    entries.Count);
        }
    }
}
